/**
 * @author nttdocomo
 */
define(function(require) {
	aries.klass("aries.util.Observable", aries.Class.extend({
		initialize : function(config) {
			var me = this;

			$.extend(me, config);

			// The subclass may have already initialized it.
			if (!me.hasListeners) {
				me.hasListeners = new me.HasListeners();
			}

			me.events = me.events || {};
			if (me.listeners) {
				me.on(me.listeners);
				me.listeners = null;
				//Set as an instance property to pre-empt the prototype in case any are set there.
			}

			if (me.bubbleEvents) {
				me.enableBubble(me.bubbleEvents);
			}
		},

		/**
		 * @private
		 * Creates an event handling function which refires the event from this object as the passed event name.
		 * @param newName
		 * @param {Array} beginEnd (optional) The caller can specify on which indices to slice
		 * @returns {Function}
		 */
		createRelayer : function(newName, beginEnd) {
			var me = this;
			return function() {
				return me.trigger.apply(me, [newName].concat(Array.prototype.slice.apply(arguments, beginEnd || [0, -1])));
			};
		},

		/**
		 * Relays selected events from the specified Observable as if the events were fired by `this`.
		 *
		 * For example if you are extending Grid, you might decide to forward some events from store.
		 * So you can do this inside your initComponent:
		 *
		 *     this.relayEvents(this.getStore(), ['load']);
		 *
		 * The grid instance will then have an observable 'load' event which will be passed the
		 * parameters of the store's load event and any function fired with the grid's load event
		 * would have access to the grid using the `this` keyword.
		 *
		 * @param {Object} origin The Observable whose events this object is to relay.
		 * @param {String[]} events Array of event names to relay.
		 * @param {String} [prefix] A common prefix to attach to the event names. For example:
		 *
		 *     this.relayEvents(this.getStore(), ['load', 'clear'], 'store');
		 *
		 * Now the grid will forward 'load' and 'clear' events of store as 'storeload' and 'storeclear'.
		 */
		relayEvents : function(origin, events, prefix) {
			prefix = prefix || '';
			var me = this, len = events.length, i = 0, oldName, newName;

			for (; i < len; i++) {
				oldName = events[i];
				newName = prefix + oldName;
				me.events[newName] = me.events[newName] || aries.emptyFn;
				origin.on(oldName, me.createRelayer(newName));
			}
		}
	}, {
		/**
		 * Removes **all** added captures from the Observable.
		 *
		 * @param {Ext.util.Observable} o The Observable to release
		 * @static
		 */
		releaseCapture : function(o) {
			o.fireEvent = this.prototype.fireEvent;
		},

		/**
		 * Starts capture on the specified Observable. All events will be passed to the supplied function with the event
		 * name + standard signature of the event **before** the event is fired. If the supplied function returns false,
		 * the event will not fire.
		 *
		 * @param {Ext.util.Observable} o The Observable to capture events from.
		 * @param {Function} fn The function to call when an event is fired.
		 * @param {Object} scope (optional) The scope (`this` reference) in which the function is executed. Defaults to
		 * the Observable firing the event.
		 * @static
		 */
		capture : function(o, fn, scope) {
			o.fireEvent = Ext.Function.createInterceptor(o.fireEvent, fn, scope);
		},

		/**
		 * Sets observability on the passed class constructor.
		 *
		 * This makes any event fired on any instance of the passed class also fire a single event through
		 * the **class** allowing for central handling of events on many instances at once.
		 *
		 * Usage:
		 *
		 *     Ext.util.Observable.observe(Ext.data.Connection);
		 *     Ext.data.Connection.on('beforerequest', function(con, options) {
		 *         console.log('Ajax request made to ' + options.url);
		 *     });
		 *
		 * @param {Function} c The class constructor to make observable.
		 * @param {Object} listeners An object containing a series of listeners to add. See {@link #addListener}.
		 * @static
		 */
		observe : function(cls, listeners) {
			if (cls) {
				if (!cls.isObservable) {
					Ext.applyIf(cls, new this());
					this.capture(cls.prototype, cls.fireEvent, cls);
				}
				if (Ext.isObject(listeners)) {
					cls.on(listeners);
				}
			}
			return cls;
		},

		/**
		 * Prepares a given class for observable instances. This method is called when a
		 * class derives from this class or uses this class as a mixin.
		 * @param {Function} T The class constructor to prepare.
		 * @private
		 */
		prepareClass : function(T, mixin) {
			// T.hasListeners is the object to track listeners on class T. This object's
			// prototype (__proto__) is the "hasListeners" of T.superclass.

			// Instances of T will create "hasListeners" that have T.hasListeners as their
			// immediate prototype (__proto__).

			if (!T.HasListeners) {
				// We create a HasListeners "class" for this class. The "prototype" of the
				// HasListeners class is an instance of the HasListeners class associated
				// with this class's super class (or with Observable).
				var Observable = Ext.util.Observable, HasListeners = function() {
				}, SuperHL = T.superclass.HasListeners || (mixin && mixin.HasListeners) || Observable.HasListeners;

				// Make the HasListener class available on the class and its prototype:
				T.prototype.HasListeners = T.HasListeners = HasListeners;

				// And connect its "prototype" to the new HasListeners of our super class
				// (which is also the class-level "hasListeners" instance).
				HasListeners.prototype = T.hasListeners = new SuperHL();
			}
		}
	}).extend(Backbone.Events))
})
